package cmp.compile.panels;

/**********************************************************
 * Calls the various other panels for display, plus defines
 * the actions for: 
 * 		Species: Create Database and Add/Edit Keep
 * 		Method:  Add New Method and Add/Edit Keep
 * 		Blast:   just calls it
 * 
 * Routines to read HOSTS.cfg and other database things
 * Routines to read mTCW.cfg (previous CPAVE.cfg) in order to populate this panel
 * Routines to get values from the other panels to pass on to other methods
 * Routines to update database with input from Edit panels
 */
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.ResultSet;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import util.Debug.ErrorReport;
import util.methods.BlastArgs;
import util.methods.HostsCfg;
import util.methods.PaveProps;
import util.methods.PropType;
import cmp.compile.CompileMain;
import assem.DBConn;
import cmp.main.Globals;
import cmp.viewer.ViewerFrame;

public class CompilePanel extends JPanel {
	private static final long serialVersionUID = 8981060324149115578L;
    static public HostsCfg hosts;
    
	public CompilePanel(ViewerFrame parentFrame, Vector<String> hostList) {		
		hosts = new HostsCfg();
		
		theParentFrame = parentFrame;
		
		pnlMain = new JPanel();
		pnlMain.setLayout(new BoxLayout(pnlMain, BoxLayout.PAGE_AXIS));
		pnlMain.setBackground(Globals.BGCOLOR);
		
		pnlProject = new ProjectPanel(this);
		pnlMain.add(pnlProject);
		
		pnlSpecies = new SpeciesPanel(this);
		pnlMain.add(pnlSpecies);
		
		pnlBlast = new BlastPanel(this);
		pnlMain.add(pnlBlast);
		
		pnlMethod = new MethodPanel(this);
		pnlMain.add(pnlMethod);
		pnlMain.add(Box.createVerticalStrut(5));
		
		pnlEditSpecies = new EditSpeciesPanel(this);
		pnlEditSpecies.setVisible(false);
		pnlMain.add(pnlEditSpecies);
		
		pnlEditMethod = new EditMethodPanel(this);
		pnlEditMethod.setVisible(false);
		pnlMain.add(pnlEditMethod);	
		
		btnRunViewer = new JButton("Launch viewMultiTCW");
		btnRunViewer.setBackground(Globals.LAUNCHCOLOR);
		btnRunViewer.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Thread buildThread = new Thread(new Runnable() {
					public void run() {
						try {
							final String JAR_DIR = "java/jars";
							final String JAR = JAR_DIR + "/mtcw.jar";
							final String MAXMEM = "2048";
							final String classPath = JAR + ":" + JAR_DIR + "/mysql-connector-java-5.0.5-bin.jar";
							final String cmd = "java -Xmx" + MAXMEM + "m -cp " + classPath + " cmp.viewer.ViewerMain " + pnlProject.getDBName();

							System.err.println("Launching viewMultiTCW for " + pnlProject.getDBName());
							Process pr = Runtime.getRuntime().exec(cmd);
							pr.getOutputStream().flush();
							OutputHandler oh = new OutputHandler(pr.getErrorStream());
							InputHandler ih = new InputHandler(pr.getOutputStream());
							
							oh.start();
							ih.start();
							
							pr.waitFor();
							
							oh.Stop();
							ih.Stop();
						} catch (Exception e) {
							ErrorReport.prtReport(e, "Error launching viewMultiTCW");
						}
					}
				});
				buildThread.setPriority(Thread.MIN_PRIORITY);
				buildThread.start();
			}
		});
		pnlMain.add(btnRunViewer);
		
		pnlMain.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		pnlMain.setMaximumSize(pnlMain.getMinimumSize());
		pnlMain.setPreferredSize(pnlMain.getMaximumSize());
		
		mainPane = new JScrollPane(pnlMain);
		parentFrame.getContentPane().add(mainPane);

		setProjectSelected(false);
	}
    
    /***************************************************************
     * Database routines
     */
	static public Vector<String> loadDBListFromHost(String hostURL, String user, String pass, 
	        String prefixold, String prefix) {
		Vector<String> retVal = new Vector<String> ();
		try {
			DBConn conn = new DBConn(hostURL, "", user, pass);
			ResultSet rs = conn.executeQuery("show databases");
				
			String dbName;
			while (rs.next()) {
				dbName = rs.getString(1);
				if ((dbName.startsWith(prefix) || dbName.startsWith(prefixold))
				        && !dbName.startsWith("PAVE_web") && !dbName.endsWith("TEMP")) {
					retVal.add(dbName);
				}
			}
			if (conn != null) conn.close();
		} 
		catch (Exception err) {
			ErrorReport.die(err, "Error accessing database on " + hostURL); 
		}
		return retVal;
	}

	public boolean isDatabaseLoaded() {
		
		String dbName = getDBName();
			
		return DBConn.checkMysqlDB("runMulti panel ", hosts.host, dbName, hosts.user, hosts.pass);
	}

	/*************************************************************
	 * methods to immediately update database
	 */
    public void updateMethodRemark(String prefix, String remark) {
    		if (!isDatabaseLoaded()) return;
		String dbName = getDBName();    	
		try {
			DBConn conn =  hosts.getDBConn(dbName);
			conn.executeUpdate("UPDATE pog_method SET description='" + remark + "' WHERE prefix='" + prefix + "'");
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error updating method");
		}
    }
    
    public void updateSpeciesRemark(String assembly, String remark) {
    	if (!isDatabaseLoaded()) return;
		String dbName = getDBName();
		try {
			DBConn conn =  hosts.getDBConn(dbName);
			if(conn.tableHasColumn("assembly", "remark"))
				conn.executeUpdate("UPDATE assembly SET remark='" + remark + "' WHERE ASMstr='" + assembly + "'");
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error updating remark");
		}
    }
    
    /********************************************************
     * get all prefixes from database -- called by ProjectPanel and MethodPanel
     */
	static public Vector<String> getLoadedMethodPrefixes(String dbName) {
		Vector<String> retVal = new Vector<String> ();
		try {
			
			DBConn conn =  hosts.getDBConn(dbName);
			
			ResultSet rset = conn.executeQuery("SELECT prefix FROM pog_method");
			while(rset.next()) {
				retVal.add(rset.getString(1));
			}
			rset.close();
			conn.close();			
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error validating db existing");
		}
		return retVal;
	}

	public ProjectPanel getProjectPanel() { return pnlProject; }
	public SpeciesPanel getSpeciesPanel() { return pnlSpecies; }
	public BlastPanel getBlastPanel() { return pnlBlast; }
	public EditSpeciesPanel getEditSpeciesPanel() { return pnlEditSpecies; }
	public MethodPanel getMethodPanel() { return pnlMethod; }
	public EditMethodPanel getEditMethodPanel() { return pnlEditMethod; }
	public ViewerFrame getParentFrame() { return theParentFrame; }
	
	public void setProjectSelected(boolean selected) {
		pnlProject.setProjectSelected(selected);
		pnlSpecies.setProjectSelected(selected);
		pnlMethod.setProjectSelected(selected);
		pnlBlast.setProjectSelected(selected);
		btnRunViewer.setEnabled(selected);
	}
	
	public void setMainPanelVisible(boolean visible) {
		pnlProject.setVisible(visible);
		pnlSpecies.setVisible(visible);
		pnlMethod.setVisible(visible);
		pnlBlast.setVisible(visible);
		btnRunViewer.setVisible(visible);
	}
	
	public void resizedEvent(ComponentEvent e) {
		pnlSpecies.resizeEvent(e);
		pnlMethod.resizeEvent(e);		
		pnlMethod.setSize(pnlSpecies.getSize());
	}
	
	//Data access for panels
	public boolean isValid() {
		boolean valid = true;
		
		if(pnlSpecies != null && pnlProject != null) {
			if(pnlSpecies.getNumRows() == 0) {
				System.out.println("Error - No species loaded");
				valid = false;
			}
			if(pnlProject.getDBName().length() == 0) {
				System.out.println("Error - No database name");
				valid = false;
			}
		}
		return valid;
	}
	
	public void allowExecute(boolean allow) {
		pnlSpecies.setCreateDatabaseEnabled(allow);
	}
	/**********************************************************
	 * Instead of having other methods have direct access to the Species and Method panels,
	 * they go through these get routines
	 */
	//Species/Assembly 
	public int getAssemblyCount() { return pnlSpecies.getNumRows(); }
	public String getAssemblyHost(int index) { 
		return pnlSpecies.getHostAt(index);  
		}
	public String getAssemblyDB(int index) { return pnlSpecies.getDBNameAt(index); }
	public String getAssemblyName(int index) { return pnlSpecies.getAssemNameAt(index); }
	public boolean isProteinDB(int index) { return pnlSpecies.isPeptideDBAt(index);}
	public String getAssemblyProteinFile(int index) { 
		String file = pnlSpecies.getProteinFileAt(index); 
		if (file.startsWith(Globals.Compile.AAfiles) || !file.contains("/")) 
			file = getCurProjectDirectory() + file;
		return file;
	}
	public String getAssemblySMATFile(int index) { 
		String smat = pnlSpecies.getSMATFileAt(index); 
		if (!smat.contains("/")) 
			smat = Globals.Compile.SMATDIR + "/" + smat; 
		return smat; 
	}
	public String getAssemblyRemark(int index) { return pnlSpecies.getRemarkAt(index); }
	public String getAssemblyPrefix(int index) { return pnlSpecies.getPrefixAt(index); } 
	
	//Methods
	public int getMethodCount() { return pnlMethod.getNumRows(); }
	public String getMethodNameAt(int index) { return pnlMethod.getMethodNameAt(index); }
	public String getMethodTypeAt(int index) { return pnlMethod.getMethodTypeAt(index); }
	public String getMethodComment(int index) { return pnlMethod.getCommentAt(index); }
	public String getMethodPrefix(int index) { return pnlMethod.getMethodPrefixAt(index); }
	public String getMethodSettings(int index) { return pnlMethod.getSettingsAt(index); }
	public boolean getMethodIsLoaded(int index) { return pnlMethod.isMethodLoadedAt(index); }
		
	public void updateStatus(String status, boolean isError) {};
	
	public String getProjectName() { return pnlProject.getSelectedProjectName(); }
	public String getDBName() { return pnlProject.getDBName(); }

	public static String getProjectDirectory() { return Globals.PROJECTDIR; }
	public static String getSMATDirectory() { return Globals.Compile.SMATDIR; }
	public String getCurrentProjectDirectory() { 
		File temp = new File(Globals.PROJECTDIR + "/" + pnlProject.getSelectedProjectName() + "/");
		return temp.getAbsolutePath();
	}
	public String getCurProjectDirectory() { 
		return Globals.PROJECTDIR + "/" + pnlProject.getSelectedProjectName() + "/";
	}
	
	//Generates pre-formatted panel for the UI - used by everything
	public static JPanel getPanel(boolean forRow) {
		JPanel retVal = new JPanel();
		if(forRow)
			retVal.setLayout(new BoxLayout(retVal, BoxLayout.LINE_AXIS));
		else
			retVal.setLayout(new BoxLayout(retVal, BoxLayout.PAGE_AXIS));
		retVal.setAlignmentX(Component.LEFT_ALIGNMENT);
		retVal.setBackground(Globals.BGCOLOR);
		return retVal;
	}
	/***********************************************************
	 * existing projects on disk
	 */
    public static String [] getProjectNames() {
	    	File dir = new File(Globals.PROJECTDIR + "/");
	    	File temp = null;
	    	
	    	if(!dir.exists())
	    		dir.mkdir();
	    	
	    	Vector<String> results = new Vector<String> ();
	    	
	    	String [] allFiles = dir.list();
	    	for(int x=0; x<allFiles.length; x++) {
	    		temp = new File(Globals.PROJECTDIR + "/" + allFiles[x]);
	    		if(temp.isDirectory())
	    			results.add(allFiles[x]);
	    	}    	
	    	return results.toArray(new String[results.size()]);
    }
    
    /********************************************************
     * routines for loading and saving mTCW.cfg
     */
	public boolean loadCPAVE() {
		try{
			PaveProps props = new PaveProps(PropType.Cmp); 
			File cfg = getCfgFileObj(true);
			if(cfg == null) return false;
			CompileMain.Print("\nLoading " + cfg.getAbsolutePath());
			clearDataFromProperties(props);
			props.loadCmp(cfg);	
			setDataFromProperties(props);
			pnlBlast.setBlastSummary();
		} catch(Exception e) {
			ErrorReport.reportError(e);
			return false;
		}
		return true;
	}
	
	//Needed when id values aren't there, simply return empty string instead of exception
	private String getPropertyVal(PaveProps props, String key) {
		try {
			return props.getProperty(key);
		}
		catch(Exception e) {
			return "";
		}
	}
	
	private void clearDataFromProperties(PaveProps props) {
		try {
			//Clear data before setting values
			if(props.containsKey("CPAVE_db")) props.setProperty("CPAVE_db", "");
			if(props.containsKey("CPAVE_host")) props.setProperty("CPAVE_host", "");
			if(props.containsKey("CPAVE_blast")) props.setProperty("CPAVE_blast", "");
			if(props.containsKey("CPAVE_blast_params")) props.setProperty("CPAVE_blast_params", "");
			if(props.containsKey("CPAVE_blast_filter")) props.setProperty("CPAVE_blast_filter", "");
			
			pnlMethod.clearData();
			
			boolean doneRead = false;
			for(int x=1; !doneRead; x++) {
				if(props.containsKey("POG_method_name"+x)) {
					props.setProperty("POG_method_name"+x, "");
					props.setProperty("POG_method_type"+x, "");
					props.setProperty("POG_method_prefix"+x, "");
					props.setProperty("POG_file"+x, "");
					props.setProperty("POG_comment"+x, "");
					props.setProperty("POG_settings"+x, "");
				}
				else
					doneRead = true;
			}
			
			pnlSpecies.clearData();
			doneRead = false;
			for(int x=1; !doneRead; x++) {
				if(props.containsKey("PAVE_db"+x)) {
					props.setProperty("PAVE_db"+x, "-");
					props.setProperty("PAVE_assem"+x, "");
					props.setProperty("PAVE_host"+x, "");
					props.setProperty("PAVE_AAFile"+x, "");
					props.setProperty("PAVE_remark"+x, "");
				}
				else
					doneRead = true;
			}
		} catch(Exception e) {
			ErrorReport.reportError(e);
		}
	}	
	
	private void setDataFromProperties(PaveProps props) {
		try {
			//Clear data before setting values
			if(props.containsKey("CPAVE_db")) pnlProject.setDBName(props.getProperty("CPAVE_db"));
			if(props.containsKey("CPAVE_GO_DB")) CompileMain.strGODB = props.getProperty("CPAVE_GO_DB");
			//if(props.containsKey("CPAVE_host")) pnlProject.setHostName(props.getProperty("CPAVE_host"));
			if(props.containsKey("CPAVE_blast")) 
			{
				String file = props.getProperty("CPAVE_blast").trim();
				if (file.length() == 0)
				{
					file = pnlBlast.getDefaultBlastTextEntry();
				}
				pnlBlast.setBlastFile(file);
			}
			if(props.containsKey("CPAVE_blast_params")) pnlBlast.setBlastParams(props.getProperty("CPAVE_blast_params"));
			if(props.containsKey("CPAVE_blast_filter")) {
				String vals = props.getProperty("CPAVE_blast_filter");
				
				String [] parseVals = vals.split("\\s+");
				
				if(parseVals.length == 3) {
					pnlBlast.setFilterBlast(parseVals[0].equals("1"));
					pnlBlast.setFilterID(parseVals[1]);
					pnlBlast.setFilterExtra(parseVals[2]);
				}
			}			
			pnlMethod.clearData();
			
			String val = "";
			boolean doneRead = false;
			for(int x=1; !doneRead; x++) {
				if(props.containsKey("POG_method_name"+x)) {
					val = props.getProperty("POG_method_name"+x);
					if(!val.equals("")) {
						String comment = getPropertyVal(props, "POG_comment"+x);
						String settings = getPropertyVal(props, "POG_settings"+x);
						String methodType = getPropertyVal(props, "POG_method_type"+x);
						String methodPrefix = getPropertyVal(props, "POG_method_prefix"+x);
						
						pnlMethod.addRow(val, methodType, methodPrefix, comment, settings);
					}
					else
						doneRead = true;
				}
				else
					doneRead = true;
			}
			//update UI with new methods
			pnlMethod.updateTable();
			
			pnlSpecies.clearData();
			doneRead = false;
			for(int x=1; !doneRead; x++) {
				if(props.containsKey("PAVE_db"+x)) {
					val = props.getProperty("PAVE_db"+x);
					if(!val.equals("-")) {
						String assem = getPropertyVal(props, "PAVE_assem"+x);
						String prefix = getPropertyVal(props, "PAVE_prefix"+x);
						String host = getPropertyVal(props, "PAVE_host"+x);
						String proteinFile = getPropertyVal(props, "PAVE_AAFile"+x);
						if(proteinFile.equals("-"))
							proteinFile = "";
						String SMATFile = getPropertyVal(props, "PAVE_SMATFile"+x);
						if(SMATFile.equals("-"))
							SMATFile = "";
						String remark = getPropertyVal(props, "PAVE_remark"+x);
						//If both SMAT and protein files are not specified, it is a peptide db
						boolean isPeptide = (proteinFile.trim().length() == 0 && SMATFile.trim().length() == 0) ||
												proteinFile.trim().equals(Globals.PROTEINsTCW);
						pnlSpecies.addRow(host, val, prefix, assem, proteinFile, SMATFile, remark, SMATFile.length() > 0, 
								isPeptide);
					}
					else
						doneRead = true;
				}
				else
					doneRead = true;
			}
			pnlSpecies.updateTable();
		} catch(Exception e) {
			ErrorReport.reportError(e);
		}
	}
	
	// called by runMultiTCW interface and at end of Create Database to create mTCW.cfg
	public boolean saveCPAVE() {
		try{
			File cfg = getCfgFileObj(false);				
			//If database does not have the prefix CMPPAVE_, add it and tell the user
			if(!pnlProject.getDBName().startsWith(Globals.CMPDBPREFIX)) {
				System.out.println("Converting database name from " + pnlProject.getDBName() + " to " + 
				        Globals.CMPDBPREFIX + "-" + pnlProject.getDBName());
				pnlProject.setDBName(Globals.CMPDBPREFIX + "_" + pnlProject.getDBName());
			}
			PrintWriter out = new PrintWriter(cfg.getAbsoluteFile());
			out.print("CPAVE_db = " + pnlProject.getDBName() + "\n");
			out.print("CPAVE_blast = " + pnlBlast.getBlastFileForCfgFile() + "\n");
			out.print("CPAVE_blast_filter = " + (pnlBlast.filterBlast()?1:0) + " " + pnlBlast.getFilterID() + " " + pnlBlast.getFilterExtra() + "\n");
			
			if(!pnlBlast.getBlastParams().equals(BlastArgs.getBlastxOpDefaults()))
				out.print("CPAVE_blast_params = " + pnlBlast.getBlastParams() + "\n");
			out.print("\n");
			out.flush();
			int id = 1;
			for(int x=0; x<pnlSpecies.getNumRows(); x++) {
				out.print("PAVE_db" + id + " = " + pnlSpecies.getDBNameAt(x) + "\n");
				out.print("PAVE_assem" + id + " = " + pnlSpecies.getAssemNameAt(x) + "\n");
				//out.print("PAVE_host" + id + " = " + pnlSpecies.getHostAt(x) + "\n");
				out.print("PAVE_AAFile" + id + " = " + pnlSpecies.getProteinFileAt(x) + "\n");
				out.print("PAVE_SMATFile" + id + " = " + pnlSpecies.getSMATFileAt(x) + "\n");
				out.print("PAVE_remark" + id + " = " + pnlSpecies.getRemarkAt(x) + "\n");
				out.print("PAVE_prefix" + id + " = " + pnlSpecies.getPrefixAt(x) + "\n");
				out.print("\n");
				out.flush();
				id++;
			}
			id = 1;
			for(int x=0; x<pnlMethod.getNumRows(); x++) {
				out.print("POG_method_name" + id + " = " + pnlMethod.getMethodNameAt(x) + "\n");
				out.print("POG_method_type" + id + " = " + pnlMethod.getMethodTypeAt(x) + "\n");
				out.print("POG_method_prefix" + id + " = " + pnlMethod.getMethodPrefixAt(x) + "\n");
				out.print("POG_comment" + id + " = " + pnlMethod.getCommentAt(x) + "\n");
				out.print("POG_settings" + id + " = " + pnlMethod.getSettingsAt(x) + "\n");
				out.print("\n");
				out.flush();
				id++;
			}
			out.close();
		} catch(Exception e) {
			ErrorReport.reportError(e);
			return false;
		}
		return true;
	}
	
	private File getCfgFileObj(boolean read) {
		String projName = pnlProject.getSelectedProjectName();
		String cfgFile = Globals.PROJECTDIR + "/" + projName + "/" + Globals.CONFIG_FILE;	
		File cfgFileObj = new File(cfgFile);
		
		if (read && ! cfgFileObj.exists()) {
			CompileMain.PrtError("Could not find configuration file " + cfgFile);
			return null;
		}
		return cfgFileObj;
	}

	public class OutputHandler extends Thread
	{
	    InputStream is;
	    
	    public OutputHandler(InputStream is)
	    {
	        this.is = is;
	    }
	    
	    public void Stop() {
		    	try {
		    		if(is != null) is.close();
		    	}
		    	catch(Exception e) {	}
	    }
	    
	    public void run()
	    {
	        try
	        {
				int c;
				while((c = is.read()) != -1) {
					System.err.print((char)c);
					System.err.flush();
				}
	        } catch (Exception e) {
	        	
	        }
	    }
	}
	public class InputHandler extends Thread
	{
	    boolean keepRunning = true;
	    BufferedReader inRead;
	    InputStreamReader isr;
	    BufferedWriter osw;
	    
	    public InputHandler(OutputStream os) {
	        isr = new InputStreamReader(System.in);
	        osw = new BufferedWriter(new OutputStreamWriter(os));
	    }
	    
	    public void Stop() {
	    		keepRunning = false;
	    }
	    
	    public void run()
	    {
	        try {
		        	while (keepRunning)
		        	{
		        		if (isr.ready())
		        		{
		        			int c = isr.read();
		        			osw.write(c);
		        			osw.flush();
		        		}
		        		Thread.sleep(100);
		        	}
		        	osw.close();
		        	inRead.close();
	        } 
	        catch (Exception e) {}
	    }
	}

	private ViewerFrame theParentFrame = null;
	private JScrollPane mainPane = null;
	
	private ProjectPanel pnlProject = null;
	private SpeciesPanel pnlSpecies = null;
	private MethodPanel pnlMethod = null;
	public BlastPanel pnlBlast = null;
	private JButton btnRunViewer = null;
	
	private EditSpeciesPanel pnlEditSpecies = null;
	private EditMethodPanel pnlEditMethod = null;
	
	private JPanel pnlMain = null;	
}